package org.dromara.im.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mqtt.MqttConfig;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.translation.core.impl.OssUrlTranslationImpl;
import org.dromara.im.contant.IMConstant;
import org.dromara.im.domain.Friend;
import org.dromara.im.domain.IMPrivateMessage;
import org.dromara.im.domain.IMUserInfo;
import org.dromara.im.domain.PrivateMessage;
import org.dromara.im.domain.dto.PrivateMessageDTO;
import org.dromara.im.domain.vo.PrivateMessageVO;
import org.dromara.im.enums.IMTerminalType;
import org.dromara.im.enums.MessageStatus;
import org.dromara.im.enums.MessageType;
import org.dromara.im.mapper.PrivateMessageMapper;
import org.dromara.im.service.IFriendService;
import org.dromara.im.service.IPrivateMessageService;
import org.dromara.im.util.BeanUtils;
import org.dromara.im.util.SensitiveFilterUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper, PrivateMessage> implements IPrivateMessageService {

    private final IFriendService friendService;
    private final MqttConfig mqttPushClient;
    private final SensitiveFilterUtil sensitiveFilterUtil;
    @Resource
    private OssUrlTranslationImpl ossUrlTranslation;
@Value("${mqtt.topic}")
private String topic;
    @Override
    public Long sendMessage(PrivateMessageDTO dto) {
        LoginUser session = LoginHelper.getLoginUser();
        Boolean isFriends = friendService.isFriend(session.getUserId(), dto.getRecvId());
        if (Boolean.FALSE.equals(isFriends)) {
            throw new ServiceException("您已不是对方好友，无法发送消息");
        }
        // 保存消息
        PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
        msg.setSendId(session.getUserId());
        msg.setStatus(MessageStatus.UNSEND.code());
        msg.setSendTime(new Date());
        this.save(msg);
        // 过滤消息内容
        String content = sensitiveFilterUtil.filter(dto.getContent());
        msg.setContent(content);
        // 推送消息
        IMPrivateMessage sendMessage = new IMPrivateMessage();
        sendMessage.setId(session.getUserId());
        sendMessage.setHeadImage(ossUrlTranslation.translation(session.getAvatar().toString(),""));
        sendMessage.setNickName(session.getNickname());
        sendMessage.setContent(content);
        mqttPushClient.publish(0, false,topic+dto.getRecvId(), JSON.toJSONString(sendMessage));
        log.info("发送私聊消息，发送id:{},接收id:{}，内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent());
        return msg.getId();
    }

    @Override
    public void recallMessage(Long id) {
        LoginUser session = LoginHelper.getLoginUser();
        PrivateMessage msg = this.getById(id);
        if (Objects.isNull(msg)) {
            throw new ServiceException("消息不存在");
        }
        if (!msg.getSendId().equals(session.getUserId())) {
            throw new ServiceException( "这条消息不是由您发送,无法撤回");
        }
        if (System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000) {
            throw new ServiceException( "消息已发送超过5分钟，无法撤回");
        }
        // 修改消息状态
        msg.setStatus(MessageStatus.RECALL.code());
        this.updateById(msg);
        // 推送消息
        PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
        msgInfo.setType(MessageType.RECALL.code());
        msgInfo.setSendTime(new Date());
        msgInfo.setContent("对方撤回了一条消息");

        IMPrivateMessage sendMessage = new IMPrivateMessage();
        sendMessage.setId(session.getUserId());
        sendMessage.setHeadImage(session.getAvatar().toString());
        sendMessage.setNickName(session.getNickname());
        sendMessage.setContent("对方撤回了一条消息");
        mqttPushClient.publish(0, false,topic+msg.getRecvId(), JSON.toJSONString(sendMessage));

        // 推给自己其他终端
        msgInfo.setContent("你撤回了一条消息");
        sendMessage.setSendToSelf(true);
        sendMessage.setRecvTerminals(Collections.emptyList());
        mqttPushClient.publish(0, false,topic+msg.getSendId(), JSON.toJSONString(sendMessage));
        log.info("撤回私聊消息，发送id:{},接收id:{}，内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent());
    }


    @Override
    public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) {
        page = page > 0 ? page : 1;
        size = size > 0 ? size : 10;
        Long userId = LoginHelper.getUserId();
        long stIdx = (page - 1) * size;
        QueryWrapper<PrivateMessage> wrapper = new QueryWrapper<>();
        wrapper.lambda().and(wrap -> wrap.and(
                wp -> wp.eq(PrivateMessage::getSendId, userId)
                        .eq(PrivateMessage::getRecvId, friendId))
                .or(wp -> wp.eq(PrivateMessage::getRecvId, userId)
                        .eq(PrivateMessage::getSendId, friendId)))
                .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code())
                .orderByDesc(PrivateMessage::getId)
                .last("limit " + stIdx + "," + size);

        List<PrivateMessage> messages = this.list(wrapper);
        List<PrivateMessageVO> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class)).collect(Collectors.toList());
        Collections.reverse(messageInfos);
        log.info("拉取聊天记录，用户id:{},好友id:{}，数量:{}", userId, friendId, messageInfos.size());
        return messageInfos;
    }

    @Override
    public void pullOfflineMessage(Long minId) {
        LoginUser session = LoginHelper.getLoginUser();
//        if(!imClient.isOnline(session.getUserId())){
//            throw new ServiceException( "网络连接失败，无法拉取离线消息");
//        }
        // 查询用户好友列表
        List<Friend> friends = friendService.findFriendByUserId(session.getUserId());
        if (friends.isEmpty()) {
            // 关闭加载中标志
            this.sendLoadingMessage(false);
            return;
        }
        // 开启加载中标志
        this.sendLoadingMessage(true);
        List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
        // 获取当前用户的消息
        LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery();
        // 只能拉取最近1个月的1000条消息
        Date minDate = DateUtils.addMonths(new Date(), -1);
        queryWrapper.gt(PrivateMessage::getId, minId)
            .ge(PrivateMessage::getSendTime, minDate)
            .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code())
            .and(wrap -> wrap.and(
                    wp -> wp.eq(PrivateMessage::getSendId, session.getUserId())
                        .in(PrivateMessage::getRecvId, friendIds))
                .or(wp -> wp.eq(PrivateMessage::getRecvId, session.getUserId())
                    .in(PrivateMessage::getSendId, friendIds)))
            .orderByDesc(PrivateMessage::getId)
            .last("limit 1000");
        List<PrivateMessage> messages = this.list(queryWrapper);
        // 消息顺序从小到大
        CollectionUtil.reverse(messages);
        // 推送消息
        for(PrivateMessage m:messages ){
            IMPrivateMessage sendMessage = new IMPrivateMessage();
            sendMessage.setId(session.getUserId());
            sendMessage.setHeadImage(session.getAvatar().toString());
            sendMessage.setNickName(session.getNickname());
            sendMessage.setContent(m.getContent());
            mqttPushClient.publish(0, false,topic+LoginHelper.getUserId(), JSON.toJSONString(sendMessage));

        }
        // 关闭加载中标志
        this.sendLoadingMessage(false);
        log.info("拉取私聊消息，用户id:{},数量:{}", session.getUserId(), messages.size());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void readedMessage(Long friendId) {
        LoginUser session = LoginHelper.getLoginUser();
        // 推送消息给自己，清空会话列表上的已读数量
        PrivateMessageVO msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.READED.code());
        msgInfo.setSendId(session.getUserId());
        msgInfo.setRecvId(friendId);
        IMPrivateMessage sendMessage = new IMPrivateMessage();
        sendMessage.setId(session.getUserId());
        sendMessage.setHeadImage(session.getAvatar().toString());
        sendMessage.setNickName(session.getNickname());
        sendMessage.setContent(null);
        mqttPushClient.publish(0, false,topic+LoginHelper.getUserId(), JSON.toJSONString(sendMessage));

        // 推送回执消息给对方，更新已读状态
        msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.RECEIPT.code());
        msgInfo.setSendId(session.getUserId());
        msgInfo.setRecvId(friendId);
        sendMessage = new IMPrivateMessage();
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        mqttPushClient.publish(0, false,topic+friendId, JSON.toJSONString(sendMessage));

        // 修改消息状态为已读
        LambdaUpdateWrapper<PrivateMessage> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(PrivateMessage::getSendId, friendId)
                .eq(PrivateMessage::getRecvId, session.getUserId())
                .eq(PrivateMessage::getStatus, MessageStatus.SENDED.code())
                .set(PrivateMessage::getStatus, MessageStatus.READED.code());
        this.update(updateWrapper);
        log.info("消息已读，接收方id:{},发送方id:{}", session.getUserId(), friendId);
    }


    @Override
    public Long getMaxReadedId(Long friendId) {
        LoginUser session = LoginHelper.getLoginUser();
        LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PrivateMessage::getSendId, session.getUserId())
                .eq(PrivateMessage::getRecvId, friendId)
                .eq(PrivateMessage::getStatus, MessageStatus.READED.code())
                .orderByDesc(PrivateMessage::getId)
                .select(PrivateMessage::getId)
                .last("limit 1");
        PrivateMessage message = this.getOne(wrapper);
        if(Objects.isNull(message)){
            return -1L;
        }
        return message.getId();
    }


    private void sendLoadingMessage(Boolean isLoadding){
        LoginUser session = LoginHelper.getLoginUser();
        PrivateMessageVO msgInfo = new PrivateMessageVO();
        msgInfo.setType(MessageType.LOADDING.code());
        msgInfo.setContent(isLoadding.toString());
        IMPrivateMessage sendMessage = new IMPrivateMessage();
        sendMessage.setId(session.getUserId());
        sendMessage.setHeadImage(session.getAvatar().toString());
        sendMessage.setNickName(session.getNickname());
        sendMessage.setContent(isLoadding.toString());
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
//        mqttPushClient.publish(0, false,topic+friendId, JSON.toJSONString(sendMessage));
    }
}
